package source;

import java.util.*;
import source.view.*;

/**
 * The PlayerStatus class is responsible for keeping track of the player's 
 * resources, units, and bases.
 * 
* @author Stephen Pilon
 *
 */

public class PlayerStatus {	
	private int ore;
	private int energy;
	private int energyUsed;
	private List<Unit> units;
	private int unitIndex;
	private List<Base> bases;
	private int baseIndex;
	private int id;
	private Vector<Unit> unitsOnMissions;
	private boolean[][] explored;

	public PlayerStatus(int ore, int energy, int id, int dimension) {
		
		this.ore = ore;
		this.energy = energy;
		this.energyUsed = 0;
		// arrays to hold the Unit and Base objects
		// not sure what size these should be initialized to.
		this.units = new LinkedList<Unit>();
		this.bases = new LinkedList<Base>();
		this.unitsOnMissions = new Vector<Unit>();
		this.unitIndex = 0;
		this.baseIndex = 0;
		explored = new boolean[dimension][dimension];
		this.id = id;
	}
	
	/**
	 * Returns the player's current amount of ore.
	 * @return
	 */
	public int getOre() {
		return ore;
	}
	
	/**
	 * Sets the player's current amount of ore.
	 * @param ore
	 */
	public void setOre(int ore) {
		this.ore = ore;
	}
	
	/**
	 * Adds or subtracts from the current amount of ore
	 * @param ore
	 */
	public void modifyOre(int ore) {
		this.ore += ore;
	}
	
	/**
	 * Returns the current amount of energy.
	 * @return
	 */
	public int getEnergy() {
		return energy;
	}
	
	/**
	 * Sets the player's current amount of energy
	 * @param energy
	 */
	public void setEnergy(int energy) {
		this.energy = energy;
	}
	
	/**
	 * Returns the current amount of energy used.
	 * @return
	 */
	public int getEnergyUsed() {
		return energyUsed;
	}
	
	/**
	 * Adds or subtracts from the current amount of energy used 
	 * @param energyUsed
	 */
	public void modifyEnergyUsed(int energyUsed) {
		this.energyUsed += energyUsed;
	}
	
	/**
	 * Adds or subtracts from the current amount of energy
	 * @param energy
	 */
	public void modifyEnergy(int energy) {
		this.energy += energy;
	}
	
	/**
	 *  increment the pointer to the next unit that can move. 
	 */	
	public void selectNextUnit() {
		/* this cyles around the list when it is at the end */
		int start = this.unitIndex;
		do	{
			this.unitIndex++;
			if (this.unitIndex >= this.units.size())	this.unitIndex = 0;
			if (this.unitIndex == start) {
				MainScreen.writeToConsole("PlayerStatus: All Units have been moved.");
				break;
			}
		} while (this.getCurrentUnit().getRemainingMoves() == 0);
	}
	
	/**
	 * Returns the current unit the unit index is pointing to.
	 * @return the current unit the unit index is pointing to.
	 */
	public Unit getCurrentUnit() {
		return units.get(this.unitIndex);		
	}
	
	
	/**
	 * Increments the base index to point to the next structure
	 *
	 */
	public void selectNextStructure() {
		/* This basically cyles around. */
		this.baseIndex++;
		if (this.baseIndex >= this.bases.size())	this.baseIndex = 0;
	}
	
	/**
	 * Returns the current structure the index is pointing to
	 * @return current structure the index is pointing to
	 */
	public Base getCurrentStructure() {
		return bases.get(this.baseIndex);
	}
	
	/**
	 * Add a unit to this playerStatus' Units
	 */
	public void addUnit(Unit u){
		this.energyUsed += u.getUnitType().upkeep();
		units.add(u);
	}
	/**
	 * Add a base to this playerStatus' Bases
	 * @param b
	 */
	public void addBase(Base b){
		this.energyUsed += b.getStructureType().upkeep();
		bases.add(b);
	}
	
	/**
	 * Returns this playersStatus' ID
	 * @return this playersStatus' ID
	 */
	public int getID(){
		return id;
	}
	
	/**
	 * Removes the unit from the players' collection of units.
	 * @param unit to remove
	 */
	public void removeUnit(Unit unit){
		//TODO allow the removal of army to reflect corrent energyUsed value.
		this.energyUsed -= unit.getUnitType().upkeep();
		units.remove(unit);
		if (this.unitIndex == this.units.size())	this.unitIndex = 0;
	}
	/**
	 * Removes the base from the players' collection of units.
	 * @param b
	 */
	public void removeBase(Base b){
		this.energyUsed -= b.getStructureType().upkeep();
		bases.remove(b);
		if (this.baseIndex == this.bases.size())	this.baseIndex = 0;
	}
	
	/**
	 * Return an array of the player's units.
	 * @return an array of Unit
	 */
	public Unit[] getUnitsArray(){
		Unit[] u = new Unit[units.size()];
		return units.toArray(u);
	}
	
	/**
	 * Return an array of the player's bases.
	 * @return an array of Base
	 */
	public Base[] getBasesArray(){
		Base[] b = new Base[bases.size()];
		return bases.toArray(b);
	}
	
	/**
	 * Lets the player's units and bases know a new turn has started.
	 *
	 */
	public void nextTurn() {
		this.baseIndex = 0;
		this.unitIndex = 0;
		for (Unit u : this.units) {
			u.newTurn();
		}
		for (Base b : this.bases) {
			b.newTurn();
		}
		doUpkeep();
	}
	
	private void doUpkeep() {
		if (this.energyUsed > this.energy) {
			int energyAvailable = this.energy;
			Base[] tempBase = new Base[0];
			tempBase = this.bases.toArray(tempBase);
			
			Unit[] tempUnit = new Unit[0];
			tempUnit = this.units.toArray(tempUnit);
			
			for (Base b : tempBase) {
				energyAvailable = this.upkeepHelper(b, energyAvailable);
			}
			for (Unit u : tempUnit) {
				if (u instanceof Army) {
					Unit[] moreUnits = ((Army)u).getUnits();
					for (Unit us : moreUnits) {
						energyAvailable = this.upkeepHelper(us, energyAvailable);
					}
				}
				else {
					energyAvailable = this.upkeepHelper(u, energyAvailable);
				}
			}
		}
	}
	
	private int upkeepHelper(Entity e, int energyAvailable) {
		int upkeep = e.getUpkeep();
		energyAvailable -= upkeep;
		if (energyAvailable < 0) {
			if (!(e.takeDamage((Math.abs(energyAvailable) > upkeep) ? upkeep :
				Math.abs(energyAvailable)))) {
				switch (e.getType()) {
				case UNIT:
					((Unit)e).destroy();
					break;
				case STRUCTURE:
					((Base)e).destroy();
					break;
				}
			}
		}
		return energyAvailable;
	}
	
	public Vector<Unit> getMissions(){
		return this.unitsOnMissions;
	}
	
	public boolean missionDone(Unit u)
	{
		return this.unitsOnMissions.remove(u);
	}
	
	public boolean addMission(Tile t)
	{
		this.getCurrentUnit().setDestinationTile(t);
		return this.unitsOnMissions.add(this.getCurrentUnit());
	}
	
	public void modifyExplored(Unit u) {
		if (u.getUnitType() == UnitType.EXPLORER) {
			int vd = u.getViewDistance();
			Tile uPos = u.getPosition();
			int x = uPos.getX();
			int y = uPos.getY();
			for(int i=x-vd; i<=vd+x; i++) {
				if(i>=0 && i<explored.length) {
					for(int j=y-vd; j<=vd+y; j++) {
						if(j>=0 && j<explored.length) {
							explored[i][j] = true;
						}
					}
				}
			}
		}
	}
	
	public boolean isExplored(int x, int y) {
		if (x >= 0 && y >= 0 && x < this.explored.length && y < this.explored.length)
			return this.explored[x][y];
		else	return false;
	}
}
